/*
 * Timer.java  0.4.0 / Oct 27, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing;

import com.cute4j.core.events.EventListenerList;
import com.cute4j.swing.events.ActionEvent;
import com.cute4j.swing.events.ActionListener;

/**
 * Timer Class.
 *
 * @cute4j.native
 *    generate="header"
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class Timer {

// TODO: recheck this class  

// Static Initialization. ------------------------------------------------------

  static {
    Swing.initialize();
    _initialize();
  }

// Private Fields. -------------------------------------------------------------

  private long _delegate;
  private int delay;
  private int initialDelay;
  private boolean repeats;
  private boolean running;

// Protected Fields. -----------------------------------------------------------

  protected final EventListenerList listenerList;

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new Timer object.
   *
   * @param delay the number of milliseconds between action events
   * @param initialDelay the initial delay
   * @param listener the listener
   */
  public Timer(int delay, int initialDelay, ActionListener listener) {
    this.delay = delay;
    this.initialDelay = initialDelay;
    repeats = true;
    listenerList = new EventListenerList();
    if (listener != null) {
      addActionListener(listener); // NOPMD
    }
  }

  /**
   * Constructs new Timer object.
   *
   * @param delay the number of milliseconds between action events
   * @param listener the listener
   */
  public Timer(int delay, ActionListener listener) {
    this(delay, delay, listener);
  }

  /**
   * Constructs new Timer object.
   *
   * @param delay the number of milliseconds between action events
   */
  public Timer(int delay) {
    this(delay, null);
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Adds action listener.
   *
   * @param listener the action listener
   */
  public void addActionListener(ActionListener listener) {
    listenerList.add(ActionListener.class, listener);
  }

  /**
   * Removes action listener.
   *
   * @param listener the action listener
   */
  public void removeActionListener(ActionListener listener) {
    listenerList.remove(ActionListener.class, listener);
  }

  /**
   * Returns all action listeners.
   *
   * @return the action listener array
   */
  public ActionListener[] getActionListeners() {
    int count = listenerList.getListenerCount(ActionListener.class);
    ActionListener[] listeners = new ActionListener[count];
    if (count > 0) {
      System.arraycopy(listenerList.getListenersArray(ActionListener.class), 0,
                       listeners,  0, count);
    }
    return listeners;
  }

  /**
   * Returns delay.
   *
   * @return delay
   */
  public int getDelay() {
    return delay;
  }

  /**
   * Sets delay.
   *
   * @param delay the delay
   *
   * @throws IllegalArgumentException if (delay &lt; 0)
   */
  public void setDelay(int delay) {
    if (delay < 0) {
      throw new IllegalArgumentException("Invalid delay: " + delay);
    }
    if (this.delay != delay) {
      this.delay = delay;
      synchronized (this) {
        if (running) {
          _setInterval(_delegate, delay);
        }
      }
    }
  }

  /**
   * Return initial delay.
   *
   * @return initial delay
   */
  public int getInitialDelay() {
    return initialDelay;
  }

  /**
   * Sets delay.
   *
   * @param initialDelay the initial delay
   *
   * @throws IllegalArgumentException if (delay &lt; 0)
   */
  public void setInitialDelay(int initialDelay) {
    if (initialDelay < 0) {
      throw new IllegalArgumentException("Invalid initial delay: " +
                                         initialDelay);
    }
    this.initialDelay = initialDelay;
  }

  /**
   * Returns <tt>true</tt> if the timer is sending an action event to its
   * listeners multiple times.
   *
   * @return  <tt>true</tt> if the timer is sending an action event to its
   * listeners multiple times
   */
  public boolean isRepeats() {
    return repeats;
  }

  /**
   * If repeats is <tt>false</tt> the timer will send only one action event to
   * its listeners.
   *
   * @param repeats the repeats flag
   */
  public void setRepeats(boolean repeats) {
    if (this.repeats != repeats) {
      this.repeats = repeats;
      synchronized (this) {
        if (running) {
          _setSingleShot(_delegate, !repeats);
        }
      }
    }
  }

  /**
   * Returns <tt>true</tt> if the timer is running.
   *
   * @return <tt>true</tt> if the timer is running
   */
  public boolean isRunning() {
    synchronized (this) {
      return running;
    }
  }

  /**
   * Starts the timer.
   *
   * @param initialDelay the initial delay
   */
  public void start(int initialDelay) {
    synchronized (this) {
      if (!running) {
        running = true;
        this.initialDelay = initialDelay;
        _delegate = _create(delay, initialDelay, !repeats);
      }
    }
  }

  /**
   * Starts the timer.
   */
  public void start() {
    start(initialDelay);
  }

  /**
   * Stops the timer.
   */
  public void stop() {
    synchronized (this) {
      if (running) {
        dispose();
        running = false;
      }
    }
  }

  /**
   * Restarts the timer.
   */
  public void restart() {
    stop();
    start();
  }

// Protected Methods. ----------------------------------------------------------

  /**
   * Fires action event.
   *
   * @param e the action event
   */
  protected void fireActionPerformed(ActionEvent e) {
    int count = listenerList.getListenerCount(ActionListener.class);
    if (count > 0) {
      ActionListener[] listeners = (ActionListener[])listenerList.
                                       getListenersArray(ActionListener.class);
      for (int i = count - 1; i >= 0; i--) {
        listeners[i].actionPerformed(e);
      }
    }
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  protected void finalize() {
    dispose(); // NOPMD NOFB
  }

// Package-Private Methods. ----------------------------------------------------

  void _timeout() {
    fireActionPerformed(new ActionEvent(this));
    if (!repeats) {
      stop();
    }
  }

// Private Methods. ------------------------------------------------------------

  private void dispose() {
    if (_delegate != 0) {
      _destroy(_delegate);
      _delegate = 0;
    }
  }

// Package-Private Native Methods. ---------------------------------------------

  native long _create(int delay, int initialDelay, boolean singleShot);

  native void _destroy(long delegate);

  native void _setInterval(long delegate, int interval);

  native void _setSingleShot(long delegate, boolean singleShot);

// Private Static Native Method. -----------------------------------------------

  private static native void _initialize();

}
